require "prefabutil"

local assets = {
	Asset("ANIM", "anim/npc_fx_star_door.zip"),
}

-------------------------------------------------------------------------------
local MAX_LIGHT_FRAME = 10
local MAX_LIGHT_RADIUS = 5

-- dframes is like dt, but for frames, not time
local function OnUpdateLight(inst, dframes)
    local done
    if inst._islighton:value() then
        local frame = inst._lightframe:value() + dframes
        done = frame >= MAX_LIGHT_FRAME
        inst._lightframe:set_local(done and MAX_LIGHT_FRAME or frame)
    else
        local frame = inst._lightframe:value() - dframes
        done = frame <= 0
        inst._lightframe:set_local(done and 0 or frame)
    end

    inst.Light:SetRadius(MAX_LIGHT_RADIUS * inst._lightframe:value() / MAX_LIGHT_FRAME)

    if done then
        inst._lighttask:Cancel()
        inst._lighttask = nil
    end
end

local function OnUpdateLightColour(inst)
	inst._lighttweener = inst._lighttweener + FRAMES * 1.25
	if inst._lighttweener > 2 * PI then
		inst._lighttweener = inst._lighttweener - 2*PI
	end
	local x = inst._lighttweener
	local s = .15
	local b = 0.85
	local sin = math.sin
	inst.Light:SetColour(
		sin(x) * s + b - s, 
		sin(x + 2/3 * PI) * s + b - s, 
		sin(x - 2/3 * PI) * s + b - s) 
end

local function OnLightDirty(inst)
    if inst._lighttask == nil then
        inst._lighttask = inst:DoPeriodicTask(FRAMES, OnUpdateLight, nil, 1)
    end
    OnUpdateLight(inst, 0)

	if not TheNet:IsDedicated() then
		if inst._islighton:value() then
			if inst._lightcolourtask == nil then
				inst._lighttweener = 0
				inst._lightcolourtask = inst:DoPeriodicTask(FRAMES, OnUpdateLightColour)
			end
		elseif inst._lightcolourtask ~= nil then
			inst._lightcolourtask:Cancel()
			inst._lightcolourtask = nil
		end
	end
end

local function TurnLightOff(inst)
	inst._islighton:set(false)
	OnLightDirty(inst)
end
-------------------------------------------------------------------------------



local function play_arrive_sound(inst)
    inst.SoundEmitter:PlaySound("terraria1/eyeofterror/arrive_portal")
end

local function fx()
    local inst = CreateEntity()
    inst.entity:AddTransform()
    inst.entity:AddNetwork()
    inst.entity:AddSoundEmitter()
	inst.entity:AddLight()

    inst.entity:AddAnimState()
    inst.AnimState:SetBank("npc_fx_star_door")
    inst.AnimState:SetBuild("npc_fx_star_door")
    inst.AnimState:SetBloomEffectHandle("shaders/anim.ksh")
    inst.AnimState:SetFinalOffset(-1)

    inst.Light:SetRadius(0)
    inst.Light:SetIntensity(0.8)
    inst.Light:SetFalloff(0.7)
    inst.Light:SetColour(1, 1, 1)
    inst.Light:Enable(true)
    inst.Light:EnableClientModulation(true)

    inst:AddTag("FX")

    inst._lightframe = net_smallbyte(inst.GUID, "portalwatch._lightframe", "lightdirty")
    inst._islighton = net_bool(inst.GUID, "portalwatch._islighton", "lightdirty")
    inst._lighttask = nil
    inst._islighton:set(true)
	OnLightDirty(inst)

    ---------------------------------------------------------------------------------------
    local function GetRandomM()
        if math.random(1000) <= 500 then
            return -1
        else
            return 1
        end
    end
    ---------------------------------------------------------------------------------------
    inst.__base_fx_client_cmd_fn = function(inst,_table)
        if _table == nil or type(_table) ~= "table" then
            return
        end
        if _table.scale and _table.scale.x then
            local base_sacle = Vector3(2,0.8,2)
            if _table.orientation then 
                base_sacle = Vector3(2,2,2)
            end
            local max_sacle = _table.scale 
            local vect = Vector3(GetRandomM(),GetRandomM(),GetRandomM())
            local ret_scale = Vector3(base_sacle.x*max_sacle.x*vect.x , base_sacle.y*max_sacle.y*vect.y , base_sacle.z*max_sacle.z*vect.z)
            inst.AnimState:SetScale(ret_scale.x,ret_scale.y,ret_scale.z)
        end

        if _table.layer then
            inst.AnimState:SetLayer(_table.layer)
        end
        if _table.orientation then
            inst.AnimState:SetOrientation(_table.orientation)
        end
        if _table.sort then
            inst.AnimState:SetSortOrder(_table.sort)
        end

        if _table.appear == true then   -------------------------------------------------------------   appear
            if _table.appear_anim_speed_mult then
                inst.AnimState:SetDeltaTimeMultiplier(_table.appear_anim_speed_mult)
            end
            inst.AnimState:PlayAnimation("appear")
            inst.AnimState:PushAnimation("idle",true)
            inst.appear_task = function()
                inst:RemoveEventCallback("animover",inst.appear_task)
                inst.appear_task = nil
                if _table.idle_anim_speed_mult then
                    inst.AnimState:SetDeltaTimeMultiplier(_table.idle_anim_speed_mult)
                end
            end
            inst:ListenForEvent("animover",inst.appear_task)
        elseif _table.disappear == true then                ------------------------------------  disappear
            if _table.disappear_anim_speed_mult then
                inst.AnimState:SetDeltaTimeMultiplier(_table.disappear_anim_speed_mult)
            end
            inst.AnimState:PlayAnimation("disappear")
            inst:ListenForEvent("animover",inst.Remove)
        else    --------------------------------------------------------------------------------  idle
            if _table.idle_anim_speed_mult then
                inst.AnimState:SetDeltaTimeMultiplier(_table.idle_anim_speed_mult)
            end
            inst.AnimState:PlayAnimation("idle",true)
        end                     -------------------------------------------------------------

    end
    inst:ListenForEvent("cmd_data_set",inst.__base_fx_client_cmd_fn)
    ---------------------------------------------------------------------------------------
    inst.__net_str = net_string(inst.GUID, "npc_events_netvars","npc_fx_star_door") ----- 用json 传送数据同步

    ---------------------------------------------------------------------------------------
    inst.entity:SetPristine()

    if not TheWorld.ismastersim then   
        -----------------------------------------------------------------------------------
        ---- client
        inst:ListenForEvent("npc_fx_star_door",function()
            local str = inst.__net_str:value()
            local CMD = json.decode(str)
            inst:PushEvent("cmd_data_set",CMD)
        end)
        inst:ListenForEvent("lightdirty", OnLightDirty)

        -----------------------------------------------------------------------------------
        return inst
    end

    -- inst:AddComponent("npc_base_lib")
    inst:DoTaskInTime(0,function()
        if inst.Ready ~= true then
            inst:Remove()
        end
    end)

    inst.__set_fn = function(_,_table)
        -- print("info : Set Get",_table.disappear)
        -- _table = {
        --     pt = Vector3(0,0,0),                         ----- 出现坐标
        --     color = Vector3(255,255,255),                ----- 颜色改变       ---
        --     a = 0.1,                                     ----- 透明度改变     --- 
        --     Mult = nil, -- or true                       ----- 着色器         ---
        --     sound == nil, -- or sound addr               ----- 声音播放
        --     scale == Vector3(0,0,0)                      ----- 缩放倍率

        --     appear = nil or true                         ----- 是否播放出场动画

        --     disapear = true                              ----- 关闭并消失
        
        --     disapear_fn = function(inst)                 -----  

        --     sort = nil or number                         ----- 贴图位于图层，默认0
        --     orientation = ANIM_ORIENTATION.OnGround      ----- 切换成二维平面用的参数，ANIM_ORIENTATION.OnGround    constants.lua 里找参数
        --     layer = LAYER_WORLD_DEBUG                    ----- 二维平面用的一些参数  LAYER_WORLD_DEBUG              constants.lua 里找参数

        --     appear_anim_speed_mult = nil or number       ----- 展开时播放乘数
        --     idle_anim_speed_mult = nil or number         ----- idle时候的播放乘数
        --     disappear_anim_speed_mult  = nil or number   ----- 消失时候的播放乘数

        -- }
        -- 
        if _table == nil then
            return
        end
        if _table.pt and _table.pt.x then
            inst.Transform:SetPosition(_table.pt.x,_table.pt.y,_table.pt.z)
        end
        if _table.color and _table.color.x then
            if _table.Mult ~= true then
                inst:AddComponent("colouradder")
                inst.components.colouradder:OnSetColour(_table.color.x/255 , _table.color.y/255 , _table.color.z/255 , _table.a or 1)
            else
                inst.AnimState:SetMultColour(_table.color.x,_table.color.y, _table.color.z, _table.a or 1)
            end
        end
        
        if _table.sound and inst.SoundEmitter then
            inst.SoundEmitter:PlaySound(_table.sound)            
        end

        if _table.disappear then
            if _table.disappear_anim_speed_mult then
                inst.AnimState:SetDeltaTimeMultiplier(_table.disappear_anim_speed_mult)
            end
            TurnLightOff(inst)
            inst.AnimState:PlayAnimation("disappear")
            inst:ListenForEvent("animover",function()
                if _table.disapear_fn then
                    _table.disapear_fn(inst)
                end
                inst:Remove()
            end)
        end

        ------------------------------------------------------------------------------
        -------- 清除 function,不给 net_string 下发

        local new_cmd_table = {}
        for name, v in pairs(_table) do
            if name ~= nil and type(v) ~= "function" then
                new_cmd_table[name] = v
            end
        end
        
        local str = json.encode(new_cmd_table)
        inst.__net_str:set(str)
        inst:PushEvent("cmd_data_set",new_cmd_table)
        inst.Ready = true

    end
    inst:ListenForEvent("Set",inst.__set_fn)




    return inst
end

return Prefab("npc_fx_star_door",fx,assets)